#ifndef P2P_HOST_HPP
#define P2P_HOST_HPP



#include "../PublicKey.hpp"
#include "NodeTable.hpp"
#include "Session.hpp"
// #include "Handshake.hpp"
#include <config/config.hpp>


// The network interface
#include <muduo/net/TcpConnection.h>
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>

#include <map>
#include <memory>
#include <vector>
#include <unordered_map>
#include <string>
#include <shared_mutex>

typedef unsigned char byte;
typedef std::vector<unsigned char> bytes;

typedef KeyID NodeID;


class Host;
class Reactor;



struct NodeInfo
{
    NodeInfo() = default;
    NodeInfo(NodeID const& _id, std::string const& _address, unsigned _port):
		id(_id), address(_address), port(_port) {}
    
    std::string enode() const { return "enode://" + id.GetHex() + "@" + address + ":" + std::to_string(port); }

    NodeID id;
	std::string address;
	unsigned port;

};


class Host
{
    friend class Session;

public: 
    Host(std::unique_ptr<P2PConfig>& config, muduo::net::EventLoop*loop);

    void start();
    void stop();

    void addReactor(std::string name, std::shared_ptr<Reactor>& reactor);
    std::map<std::string, std::shared_ptr<Reactor>> const& reactors() const;
    std::shared_ptr<Reactor> reactor(std::string name);

    void setNodeInfo(NodeInfo const& nodeInfo){ nodeInfo_ = nodeInfo; }
    NodeInfo const& nodeInfo(){ return nodeInfo_; }

    void broadcast(byte chID, bytes const& msg);

private:
    std::unique_ptr<P2PConfig> config_;
    muduo::net::EventLoop * loop_;
    std::unique_ptr<muduo::net::TcpServer> server_;

    std::map<std::string, std::shared_ptr<Reactor>> reactors_;
    std::vector<unsigned char> chIDs_;
    std::map<unsigned char, std::shared_ptr<Reactor>> reactorBychID_;

    std::mutex mtx_;
    std::vector<Peer> peers_;
    
    NodeInfo nodeInfo_;
    std::shared_ptr<NodeTable> nodeTable_;

};

#endif // !P2P_HOST_HPP
